/*-----------------------------------------
file name  : fxp_div.v
created    : 2025/04/26 23:17:44
modified   : 2025-04-30 16:08:55
description: division 
notes      : 
author     : yyrwkk
-----------------------------------------*/
module fxp_div #(
    parameter WIIA = 8    ,
    parameter WIFA = 8    ,
    parameter WIIB = 8    ,
    parameter WIFB = 8    ,
    parameter WOI  = 8    ,
    parameter WOF  = 8    ,
    parameter ROUND= 1    
)(
    input  [WIIA+WIFA-1:0] dividend,
    input  [WIIB+WIFB-1:0] divisor ,
    output [WOI +WOF -1:0] out     ,
    output                 overflow
);

localparam WRI = WOI+WIIB > WIIA ? WOI+WIIB : WIIA;
localparam WRF = WOF+WIFB > WIFA ? WOF+WIFB : WIFA;

wire                 sign                       ;
wire [WIIA+WIFA-1:0] udividend                  ;
wire [WIIB+WIFB-1:0] udivisor                   ;
wire [ WRI+ WRF-1:0] acc      [WOI + WOF: 0]    ;
wire [ WRI+ WRF-1:0] divd                       ;
wire [ WRI+ WRF-1:0] divr                       ;
wire [WOI +WOF -1:0] out_unfix                  ; 

// convert dividend and divisor to positive number
assign sign      = dividend[WIIA+WIFA-1] ^ divisor[WIIB+WIFB-1]        ;
assign udividend = dividend[WIIA+WIFA-1] ? (~dividend)+ 1'b1 : dividend;
assign udivisor  = divisor [WIIB+WIFB-1] ? (~divisor )+ 1'b1 : divisor ;

fxp_zoom # (
    .WII      ( WIIA      ),
    .WIF      ( WIFA      ),
    .WOI      ( WRI       ),
    .WOF      ( WRF       ),
    .ROUND    ( 0         )
) dividend_zoom (
    .in       ( udividend ),
    .out      ( divd      ),
    .overflow (           )
);

fxp_zoom # (
    .WII      ( WIIB      ),
    .WIF      ( WIFB      ),
    .WOI      ( WRI       ),
    .WOF      ( WRF       ),
    .ROUND    ( 0         )
) divisor_zoom (
    .in       ( udivisor  ),
    .out      ( divr      ),
    .overflow (           )
);

genvar shamt;
generate  
    for(shamt=WOI-1; shamt>=-WOF; shamt=shamt-1) begin: div_proc_block
        if( shamt == WOI-1 ) begin 
            assign out_unfix[WOI-1-shamt] = (divr << shamt ) <= divd ; 
            assign acc[WOI-1-shamt] = ( out_unfix[WOI-1-shamt]) ? ( divr << shamt ) : acc[WOI-1-shamt-1] ;
        end else if( shamt >=0 ) begin 
            assign out_unfix[WOI-1-shamt] = acc[WOI-1-shamt-1] + ( divr << shamt ) <= divd ; 
            assign acc[WOI-1-shamt] =  out_unfix[WOI-1-shamt]  ? (acc[WOI-1-shamt-1] + ( divr << shamt )): acc[WOI-1-shamt-1] ;
        end else begin 
            assign out_unfix[WOI-1-shamt] = acc[WOI-1-shamt-1] + ( divr >> (-shamt)) <= divd ; 
            assign acc[WOI-1-shamt] = ( out_unfix[WOI-1-shamt] ) ? (acc[WOI-1-shamt-1] + ( divr >> (-shamt))) : acc[WOI-1-shamt-1];
        end
    end
endgenerate

wire [ WRI+ WRF-1:0] acct        ; 
wire [WOI +WOF -1:0] out_unsigned;        
assign acct          = acc[WOI + WOF] + (divr>>(WOF));
assign out_unsigned  = ( ( ROUND && ~(&out_unfix) ) & ( acct-divd < divd - acc[WOI + WOF] ) ) ? out_unfix + 1'b1 : out_unfix ; 

assign out = sign ? (  out[WOI+WOF-1]  ?  {1'b1, {( WOI+WOF-1){1'b0}} } : ( (~out_unsigned) + 1'b1)  )
             : (  out[WOI+WOF-1] ) ? {1'b0, {(WOI+WOF){1'b1}} } : out_unsigned;

assign overflow = ( sign & ( |out[WOI+WOF-1:0] ) ) | ( (~sign) &  out[WOI+WOF-1] );

// original code 
// always @ (*) begin
//     acc = 0;
//     for(shamt=WOI-1; shamt>=-WOF; shamt=shamt-1) begin
//         if(shamt>=0)
//             acct = acc + (divr<<shamt);
//         else
//             acct = acc + (divr>>(-shamt));
//         if( acct <= divd ) begin
//             acc = acct;
//             out[WOF+shamt] = 1'b1;
//         end else
//             out[WOF+shamt] = 1'b0;
//     end
//     
//     if(ROUND && ~(&out)) begin
//         acct = acc+(divr>>(WOF));
//         if(acct-divd<divd-acc)
//             out=out+1;
//     end
//     
//     overflow = 1'b0;
//     if(sign) begin
//         if(out[WOI+WOF-1]) begin
//             if(|out[WOI+WOF-2:0]) overflow = 1'b1;
//             out[WOI+WOF-1] = 1'b1;
//             out[WOI+WOF-2:0] = 0;
//         end else begin
//             out = (~out) + ONEO;
//         end
//     end else begin
//         if(out[WOI+WOF-1]) begin
//             overflow = 1'b1;
//             out[WOI+WOF-1] = 1'b0;
//             out[WOI+WOF-2:0] = {(WOI+WOF){1'b1}};
//         end
//     end
// end

endmodule

